package 
{
	//import flash
	import Box2D.Dynamics.Joints.b2DistanceJointDef;
	import Box2D.Dynamics.Joints.b2PrismaticJointDef;
	import Box2D.Dynamics.Joints.b2RevoluteJointDef;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	
	//import box2d
	import Box2D.Dynamics.b2World;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2DebugDraw;

	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	
	import Box2D.Dynamics.b2Fixture;
	import Box2D.Dynamics.b2FixtureDef;

	public class Main extends Sprite 
	{
		//world
		private var world:b2World;
		private var grav:b2Vec2;
		private const sleep:Boolean = true;
		
		private var axis:b2Vec2;
		
		//world var
		private const step:Number = 1.0 / 30.0;
		private const ppm:Number = 30;
		private const iter:Number = 6;
		
		//debug data
		private const fAlpha:Number = .6;
		private const lAlpha:Number = .8;
		private const lThick:Number = 1;
		
		//debugdraw
		private var dDraw:b2DebugDraw;
		private var dSprite:Sprite;
		
		private var isMouseDown:Boolean = false;
		
		private function makePrismaticJoint(bA:b2Body, bB:b2Body, lower:Number, upper:Number, enableLimit:Boolean = true, enableMotor:Boolean = true, maxMotorForce:Number = 1.0, motorSpeed:Number = 0.0):b2PrismaticJointDef
		{
			var pJointDef:b2PrismaticJointDef = new b2PrismaticJointDef();
			pJointDef.Initialize(bA, bB, bA.GetWorldCenter(), axis);
			pJointDef.lowerTranslation = lower;
			pJointDef.upperTranslation = upper;
			pJointDef.enableLimit = enableLimit;
			
			pJointDef.maxMotorForce = maxMotorForce;
			pJointDef.motorSpeed = motorSpeed;
			pJointDef.enableMotor = true;
			
			return pJointDef;
		}
		
		private function makeRevoluteJoint(bA:b2Body, bB:b2Body, maxMotorTorque:Number, enableMotor:Boolean):b2RevoluteJointDef
		{
			var rJointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
			rJointDef.Initialize(bA, bB, bB.GetWorldCenter());
			rJointDef.maxMotorTorque = maxMotorTorque;
			rJointDef.enableMotor = enableMotor;
			return rJointDef;
		}
		
		private function makeDistanceJointDef(bA:b2Body, bB:b2Body):b2DistanceJointDef
		{
			var dJointDef:b2DistanceJointDef = new b2DistanceJointDef();
			dJointDef.Initialize(bA, bB, bA.GetWorldCenter(), bB.GetWorldCenter());
			return dJointDef;
		}
		
		private function makeBody(bd:b2BodyDef, fd:b2FixtureDef):b2Body
		{
			var body:b2Body = world.CreateBody(bd);
			body.CreateFixture(fd);
			
			return body;
		}
		
		private function makeFixtureDef(shape:b2PolygonShape, density:Number, friction:Number):b2FixtureDef
		{
			var fd:b2FixtureDef = new b2FixtureDef();
			fd.shape = shape;
			fd.density = density;
			fd.friction = friction;
			return fd;
		}
		
		private function makeBox(w:Number, h:Number):b2PolygonShape
		{
			var box:b2PolygonShape = new b2PolygonShape();
			box.SetAsBox(w / ppm, h / ppm);
			return box;
		}
		
		private function makeBodyDef(x:Number, y:Number, isDynamic:Boolean = false):b2BodyDef
		{
			var bf:b2BodyDef = new b2BodyDef();
			bf.position.Set(x / ppm, y / ppm);
			
			if (isDynamic)
			{
				bf.type = b2Body.b2_dynamicBody;
			}
			
			return bf;
		}
		
		private function update(e:Event):void
		{
			worldstep();
		}
		
		private function worldstep():void
		{
			world.Step(step, iter, iter);
			world.ClearForces();
			world.DrawDebugData();
		}
		
		
		private function init():void
		{
			//world
			{
			grav = new b2Vec2(0.0, 10.0);
			axis = new b2Vec2(1.0, 0.0);
			world = new b2World(grav, sleep);
			}
			
			//debugdraw
			{
			dDraw = new b2DebugDraw();
			dSprite = new Sprite();
			addChild(dSprite);
			
			dDraw.SetAlpha(lAlpha);
			dDraw.SetDrawScale(ppm);
			dDraw.SetFillAlpha(fAlpha);
			dDraw.SetLineThickness(lThick);
			dDraw.SetSprite(dSprite);
			dDraw.SetFlags(b2DebugDraw.e_shapeBit);
			
			world.SetDebugDraw(dDraw);
			}
			
			addEventListener(Event.ENTER_FRAME, update);
			addEventListener(MouseEvent.MOUSE_DOWN, mousedown_handler);
			addEventListener(MouseEvent.MOUSE_UP, mouseup_handler);
		}
		
		private function mouseup_handler(e:MouseEvent):void
		{
			isMouseDown = false;
		}
		
		private function mousedown_handler(e:MouseEvent):void
		{
			isMouseDown = true;
		}
		
		private function enter():void
		{
			//bodies
			var groundBody:b2Body;
			var b0_body:b2Body;
			var b1_body:b2Body;
			
			//joints
			var prismJointDef:b2PrismaticJointDef
			//make ground
			{
			var gs:b2PolygonShape = makeBox(400, 15);
			var gbd:b2BodyDef = makeBodyDef(0, 385, false);
			var gfd:b2FixtureDef = makeFixtureDef(gs, 1, 1);
			groundBody = makeBody(gbd, gfd);
			}
			
			//make b0
			{
			var b0_shape:b2PolygonShape = makeBox(10, 10);
			var b0_bodyDef:b2BodyDef = makeBodyDef(100, 100, true);
			
			var b0_fixDef:b2FixtureDef = makeFixtureDef(b0_shape, 1, 1);
			
			b0_body = makeBody(b0_bodyDef, b0_fixDef);
			}
			
			//make b1
			{
			var b1_shape:b2PolygonShape = makeBox(10, 10);
			var b1_bodyDef:b2BodyDef = makeBodyDef(200, 100, true);
			
			var b1_fixDef:b2FixtureDef = makeFixtureDef(b1_shape, 1, 1);
			
			b1_body = makeBody(b1_bodyDef, b1_fixDef);
			}
			//world.CreateJoint( makeRevoluteJoint(b1_body, groundBody, 1.0, true));
			
			prismJointDef = makePrismaticJoint(groundBody, b0_body, -5.0, 2.5, true, true, 1.0, 0.0);
			world.CreateJoint(prismJointDef);
			
		}
		
		public function Main():void 
		{
			
			init();
			enter();
		}
		
	}
	
}